bugfix(contain): Restore retail compatibility after crash fix in OpenContain::processDamageToContained#2427
Conversation
|
| Filename | Overview |
|---|---|
| GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h | Adds processDamageToContainedInternal declaration under RETAIL_COMPATIBLE_CRC; helper is placed in the public section but should be protected/private. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Replaces the old size-change-based break logic with a snapshot copy (stack for <16, heap for ≥16), fixing premature loop exit when container size changes mid-iteration; minor size-source inconsistency between the two copy paths. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[processDamageToContained] --> B{RETAIL_COMPATIBLE_CRC?}
B -- Yes --> C[Assert m_containListSize == m_containList.size]
C --> D{m_containListSize < 16?}
D -- Yes --> E["Stack copy: Object*[16]\nstd::copy from m_containList"]
D -- No --> F["Heap copy: std::vector<Object*>\nconstructed from m_containList"]
E --> G[processDamageToContainedInternal\nsize = m_containListSize]
F --> H[processDamageToContainedInternal\nsize = containCopy.size]
G --> I[Iterate snapshot safely]
H --> I
B -- No --> J[Swap m_containList into local list\nm_containListSize = 0]
J --> K[Iterate local list applying damage]
K --> L[Swap list back into m_containList]
Prompt To Fix All With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h
Line: 222-224
Comment:
**`processDamageToContainedInternal` placed in public section**
`processDamageToContainedInternal` is a pure implementation-detail helper for `processDamageToContained`. It sits in the `public:` section (the `protected:` block starts at line 235), making it callable by unrelated code. It should be moved under `protected:` or `private:` to enforce encapsulation.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Line: 1512-1517
Comment:
**Stack path passes `m_containListSize`; heap path passes `containCopy.size()`**
The two paths pass different size values to `processDamageToContainedInternal`:
- **Stack path** (lines 1512–1517): `std::copy` copies `m_containList.size()` elements, but the count given to the internal function is `m_containListSize`.
- **Heap path** (lines 1519–1523): the count is derived from the vector (`containCopy.size()`), which equals `m_containList.size()`.
In debug builds the `DEBUG_ASSERTCRASH` at line 1505 ensures `m_containListSize == m_containList.size()`, so both paths are equivalent there. In a release build, if the invariant is ever broken, the stack path could silently skip occupants or read past the end of `containCopy`. For consistency and defensive robustness, both paths should derive their count from the same source (`m_containList.size()`).
How can I resolve this? If you propose a fix, please make it concise.Reviews (6): Last reviewed commit: "Addressed feedback." | Re-trigger Greptile
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
b533974 to
b342a9f
Compare
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
6c35643 to
4d5d684
Compare
|
I reset the branch and broke the new diff into manageable commits. |
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
|
Addressed feedback. In the unlikely scenario that the new implementation turns out not to be fully retail compatible, I expect that adding the following code here would fix it: if (size != m_containListSize)
{
if (m_containListSize == 0)
break;
if (object->isEffectivelyDead())
continue;
if (std::find(m_containList.begin(), m_containList.end(), object) == m_containList.end())
continue;
} |
The way the crash fix was implemented in #1019 assumes the container size is either unchanged or 0. This is not the case in the attached replay in #2223.
Gameplay without this PR:
processDamageToContained_before2.mp4
Gameplay with this PR:
processDamageToContained_after2.mp4
Due to a different bug, a GLA Worker enters the unmanned Emperor Overlord but becomes part of the crew instead of the pilot. When the Emperor gets destroyed it first removes its Gattling turret and so the container size has changed. The Worker is still inside, though, so breaking out of the loop is not allowed.
TODO: